/**
  ******************************************************************************
  * @file    W25Q64.c
  * @brief   W25Q64 Flash存储器驱动代码
  * @note    使用前需要在CubeMX中配置：
  *          - SPI2: 全双工主模式, 8位数据, MSB优先
  *          - SPI2 DMA: RX通道(内存到外设)和TX通道(外设到内存)
  *          - GPIO: 一个引脚作为W25Q64的片选(CS)信号
  *          - 中断: 使能SPI2和DMA中断
  ******************************************************************************
  */

#include "w25q64_hardware_spi.h"
#include "main.h"

// DMA传输完成标志
volatile uint8_t w25q64_dma_complete = 0;

// SPI句柄引用
extern SPI_HandleTypeDef hspi2;

/**
  * @brief  W25Q64初始化函数
  * @note   只需设置CS引脚，SPI2硬件初始化应在CubeMX中完成
  * @param  无
  * @retval 无
  */
void W25Q64_Init(void)
{
    // 设置CS引脚为高电平（不选中芯片）
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_SET);
}

/**
  * @brief  读取W25Q64的制造商ID和设备ID
  * @note   使用阻塞方式传输
  * @param  MID: 制造商ID指针
  * @param  DID: 设备ID指针
  * @retval 无
  */
void W25Q64_ReadID(uint8_t *MID, uint16_t *DID)
{
    uint8_t tx_data[4] = {W25Q64_JEDEC_ID, 0xFF, 0xFF, 0xFF};
    uint8_t rx_data[4];
    
    // 选中芯片
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_RESET);
    
    // 发送命令并接收响应
    HAL_SPI_TransmitReceive(&hspi2, tx_data, rx_data, 4, 1000);
    
    // 取消选中芯片
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_SET);
    
    // 解析返回的ID数据
    *MID = rx_data[1];
    *DID = (rx_data[2] << 8) | rx_data[3];
}

/**
  * @brief  写使能命令
  * @note   在执行写操作前必须调用此函数
  * @param  无
  * @retval 无
  */
void W25Q64_Write_Enable(void)
{
    uint8_t cmd = W25Q64_WRITE_ENABLE;
    
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_RESET);
    HAL_SPI_Transmit(&hspi2, &cmd, 1, 100);
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_SET);
}

/**
  * @brief  等待Flash操作完成
  * @note   轮询状态寄存器，直到BUSY位为0
  * @param  无
  * @retval 无
  */
void W25Q64_WaitBusy(void)
{
    uint8_t cmd = W25Q64_READ_STATUS_REGISTER_1;
    uint8_t status;
    
    do {
        HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_RESET);
        HAL_SPI_Transmit(&hspi2, &cmd, 1, 100);
        HAL_SPI_Receive(&hspi2, &status, 1, 100);
        HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_SET);
    } while (status & 0x01);  // 等待BUSY位为0
}

/**
  * @brief  页编程操作（写入一页数据）
  * @note   一页最大256字节，不能跨页写入
  * @param  Address: 写入地址
  * @param  DataArray: 要写入的数据数组
  * @param  Count: 写入数据长度（不超过256字节）
  * @retval 无
  */
void W25Q64_PageProgram(uint32_t Address, uint8_t *DataArray, uint16_t Count)
{
    uint8_t cmd[4] = {
        W25Q64_PAGE_PROGRAM,
        (Address >> 16) & 0xFF,
        (Address >> 8) & 0xFF,
        Address & 0xFF
    };
    
    // 写使能
    W25Q64_Write_Enable();
    
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_RESET);
    
    // 发送页编程命令和地址
    HAL_SPI_Transmit(&hspi2, cmd, 4, 100);
    // 发送数据
    HAL_SPI_Transmit(&hspi2, DataArray, Count, 1000);
    
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_SET);
    
    // 等待写入完成
    W25Q64_WaitBusy();
}

/**
  * @brief  写入数据（自动处理跨页）
  * @note   可以写入任意长度的数据，自动分页
  * @param  Address: 起始地址
  * @param  DataArray: 要写入的数据数组
  * @param  TotalCount: 总数据长度
  * @retval 无
  */
void W25Q64_WriteData(uint32_t Address, uint8_t *DataArray, uint32_t TotalCount)
{
    uint32_t remaining = TotalCount;
    uint32_t current_addr = Address;
    uint8_t *current_data = DataArray;

    while (remaining > 0) {
        // 计算当前页剩余空间
        uint32_t page_remain = 256 - (current_addr % 256);
        uint32_t write_len = (remaining < page_remain) ? remaining : page_remain;

        // 写入当前页的数据
        W25Q64_PageProgram(current_addr, current_data, write_len);

        // 更新地址和数据指针
        current_addr += write_len;
        current_data += write_len;
        remaining -= write_len;
    }
}

/**
  * @brief  扇区擦除（4KB）
  * @note   擦除指定地址所在的4KB扇区
  * @param  Address: 扇区内任意地址
  * @retval 无
  */
void W25Q64_SectorErase(uint32_t Address)
{
    uint8_t cmd[4] = {
        W25Q64_SECTOR_ERASE_4KB,
        (Address >> 16) & 0xFF,
        (Address >> 8) & 0xFF,
        Address & 0xFF
    };
    
    W25Q64_Write_Enable();
    
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_RESET);
    HAL_SPI_Transmit(&hspi2, cmd, 4, 100);
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_SET);
    
    W25Q64_WaitBusy();
}

/**
  * @brief  32KB块擦除
  * @note   擦除指定地址所在的32KB块
  * @param  Address: 块内任意地址
  * @retval 无
  */
void W25Q64_BlockErase_32K(uint32_t Address)
{
    uint8_t cmd[4] = {
        W25Q64_BLOCK_ERASE_32KB,
        (Address >> 16) & 0xFF,
        (Address >> 8) & 0xFF,
        Address & 0xFF
    };
    
    W25Q64_Write_Enable();
    
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_RESET);
    HAL_SPI_Transmit(&hspi2, cmd, 4, 100);
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_SET);
    
    W25Q64_WaitBusy();
}

/**
  * @brief  读取数据（阻塞方式）
  * @note   使用阻塞SPI传输读取数据
  * @param  Address: 读取起始地址
  * @param  DataArray: 数据存储缓冲区
  * @param  Count: 要读取的字节数
  * @retval 无
  */
void W25Q64_ReadData(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{
    uint8_t cmd[4] = {
        W25Q64_READ_DATA,
        (Address >> 16) & 0xFF,
        (Address >> 8) & 0xFF,
        Address & 0xFF
    };
    
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_RESET);
    
    // 发送读取命令和地址
    HAL_SPI_Transmit(&hspi2, cmd, 4, 100);
    // 接收数据
    HAL_SPI_Receive(&hspi2, DataArray, Count, 1000);
    
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_SET);
}

/**
  * @brief  读取数据（DMA方式）
  * @note   使用DMA传输读取数据，不阻塞CPU
  *         需要在CubeMX中配置SPI2的RX DMA通道
  * @param  Address: 读取起始地址
  * @param  DataArray: 数据存储缓冲区
  * @param  Count: 要读取的字节数
  * @retval 无
  */
void W25Q64_ReadData_DMA(uint32_t Address, uint8_t *DataArray, uint32_t Count)
{
    uint8_t cmd[4] = {
        W25Q64_READ_DATA,
        (Address >> 16) & 0xFF,
        (Address >> 8) & 0xFF,
        Address & 0xFF
    };
    
    // 重置DMA完成标志
    w25q64_dma_complete = 0;
    
    // 选中芯片
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_RESET);
    
    // 先发送命令和地址（阻塞方式）
    HAL_SPI_Transmit(&hspi2, cmd, 4, 100);
    
    // 使用DMA接收数据（非阻塞）
    HAL_SPI_Receive_DMA(&hspi2, DataArray, Count);
    
    // 等待DMA传输完成
    while(w25q64_dma_complete == 0) {
        // 此处可以添加超时检测或其他任务处理
    }
    // 注意：片选在DMA完成回调函数中自动拉高
}

/**
  * @brief  写入状态寄存器
  * @note   配置Flash的状态寄存器
  * @param  无
  * @retval 无
  */
void W25Q64_WriteStatus(void)
{
    uint8_t cmd[3] = {
        W25Q64_WRITE_STATUS_REGISTER,
        0x00,  // 状态寄存器1
        0x00   // 状态寄存器2
    };
    
    W25Q64_Write_Enable();
    
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_RESET);
    HAL_SPI_Transmit(&hspi2, cmd, 3, 100);
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_SET);
    
    W25Q64_WaitBusy();
}

/**
  * @brief  写入状态寄存器1
  * @note   专门写入状态寄存器1
  * @param  Data: 要写入的数据
  * @retval 无
  */
void W25Q64_WriteSR_1(uint8_t Data)
{
    uint8_t enable_cmd = W25Q64_SR_WRITE_ENABLE;
    uint8_t write_cmd[2] = {W25Q64_WRITE_STATUS_REGISTER_1, Data};
    
    // 使能状态寄存器写入
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_RESET);
    HAL_SPI_Transmit(&hspi2, &enable_cmd, 1, 100);
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_SET);
    
    // 写入状态寄存器
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_RESET);
    HAL_SPI_Transmit(&hspi2, write_cmd, 2, 100);
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_SET);
}

/**
  * @brief  读取状态寄存器1
  * @note   获取Flash的状态信息
  * @param  无
  * @retval 状态寄存器1的值
  */
uint8_t W25Q64_GetSR_1(void)
{
    uint8_t cmd = W25Q64_READ_STATUS_REGISTER_1;
    uint8_t status;
    
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_RESET);
    HAL_SPI_Transmit(&hspi2, &cmd, 1, 100);
    HAL_SPI_Receive(&hspi2, &status, 1, 100);
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_SET);
    
    return status;
}

/**
  * @brief  DMA传输完成回调函数
  * @note   由HAL_SPI_RxCpltCallback调用，处理DMA传输完成后的操作
  * @param  无
  * @retval 无
  */
void W25Q64_DMA_CompleteCallback(void)
{
    // 拉高片选，结束SPI传输
    HAL_GPIO_WritePin(W25Q64_CS_GPIO_Port, W25Q64_CS_Pin, GPIO_PIN_SET);
    
    // 设置DMA完成标志
    w25q64_dma_complete = 1;
}

/**
  * @brief  SPI DMA接收完成回调函数
  * @note   HAL库的SPI DMA接收完成中断回调函数
  *         需要在CubeMX中使能SPI2和DMA的中断
  * @param  hspi: SPI句柄指针
  * @retval 无
  */
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
    // 检查是否是SPI2的DMA接收完成
    if(hspi->Instance == SPI2) {
        W25Q64_DMA_CompleteCallback();
    }
}

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/